package com.zqxq.lottery.util;


import com.zqxq.common.utils.StrUtils;
import lombok.extern.slf4j.Slf4j;

import java.util.Arrays;

/**
 * 说    明：  中奖计算
 * 创 建 人：	一刀·胡
 * 创建时间：	2018/3/21 9:16
 * 修 改 人：
 * 修改日期：
 **/
@Slf4j
public abstract class  Calculator {
    // 排列组合
    public long combine(int m,int n) {
        if (m < n || n<0) {
            return 0L;
        }
        return factorial(m, m - n + 1) / factorial(n, 1);
    }

    // 阶乘
    public long factorial(long max, long min) {
        if (max >= min && max > 1) {
            return max * factorial(max - 1, min);
        } else {
            return 1L;
        }
    }

    /**
    * @Author 苦智
    * @Description 校验字符串包含 不限字符顺序
    * @Date 10:57 2018/4/17
    * @Param [minStr, maxStr]
    * @return boolean
    **/
    public boolean containsNoSort(String minStr,String maxStr){
        boolean flag = true;
        for (String s : minStr.split(",")) {
            if (maxStr.indexOf(s) < 0)
                flag = false;
        }
        return flag;
    }

    // 双色球|大乐透 计算前区或后区命中指定个数的方案注数
    /**
     * @param num       采集个数
     * @param req       固定选球
     * @param opt       可变选球
     * @param reqHit    固定命中
     * @param optHit    可变命中
     **/
    public  long[] solveHits(int num,int req,int opt,int reqHit,int optHit) {
        int optLeft = num - req, optMiss = opt - optHit, max = reqHit + optHit;
        long[] hits = new long[num+1];
        for (int i = 0; i <= num; ++ i) {
            if (i < reqHit || i > max) {
                hits[i] = 0;
            } else {
                int optNeed = i - reqHit;
                hits[i] = combine(optHit, optNeed) * combine(optMiss, optLeft - optNeed);
            }
        }
        return hits;
    }

    /**
     * 说    明：  选球命中个数
     * 创 建 人：	一刀·胡
     * 创建时间：	2018/3/27 10:21
     * 修 改 人：
     * 修改日期：
     **/
    public int[] hit(String secNum,String hitNum){
        if (StrUtils.isEmpty(secNum) || StrUtils.isEmpty(hitNum))
            return new int[]{0,0};
        int sec = secNum.split(",").length;
        int hit = 0;
        int opt[] ={sec,hit};
        String[] hitNums = hitNum.split(",");
        for(String num:hitNums){
            if(secNum.indexOf(num)!=-1)
                hit++;
        }
        opt[1] = hit;
        return opt;
    }

    /**
     * 说    明：   获取中奖描述信息
     * 创 建 人：	一刀·胡
     * 创建时间：	2018/3/27 11:22
     * 修 改 人：
     * 修改日期：
     **/
    public String winInfo(int[] win,Object[] winningsList, int betTimes) {
        if (null != win) {
            String s = "";
            Long total = 0L;
            int i = 0;
            for (int w : win) {
                if (w > 0) {
                    if (winningsList[i] instanceof Integer) {
                        int m = (Integer) winningsList[i];
                        total += w * betTimes * m;
                    } else {
                        w = w * betTimes;
                        s += w > 1 ? (w + "*" + winningsList[i]) : winningsList[i];
                        s += "+";
                    }
                }
                i++;
            }
            s += total;
            return s;
        }
        return "0";
    }

    /**
     * @Author 苦智
     * @Description 福彩、排列3常规中奖判断
     * @Date 10:49 2018/4/13
     * @Param [Str, containsStr] "1,2,|3,4,|5" "1,2,3"
     * @return boolean
     **/
    public static boolean compareContainsStr(String Str,String containsStr){
        String[] bettingNums = Str.split("\\|");
        String[] lotteryNums = containsStr.split(",");
        if (bettingNums[0].contains(lotteryNums[0]) && bettingNums[1].contains(lotteryNums[1]) && bettingNums[2].contains(lotteryNums[2]))
            return true;
        return false;
    }

    /**
     * @Author 苦智
     * @Description 字符串去重
     * @Date 17:53 2018/4/12
     * @Param [repeatStr]
     * @return java.lang.String
     **/
    public String getUnRepeatStr(String repeatStr){
        StringBuffer result = new StringBuffer();
        for(String i : repeatStr.split(",")){
            if (!result.toString().contains(i)) {
                if (result.length() > 0){
                    result.append(",");
                }
                result.append(i);
            }
        }
        return result.toString();
    }

    /**
     * @Author 苦智
     * @Description 福彩3D、排列3公共算法
     * @Date 17:44 2018/4/16
     * @Param [lotteryNum, bettingNum, lotteryId, playType] 开奖号码、投注号码、奖种、玩法
     * @return int[] 每个等级的奖项的中奖注数
     **/
    protected int[] commonCalculation(String lotteryNum, String bettingNum, String playType){
        int[] win = new int[4];
        switch (Integer.parseInt(playType)){
            case 1:
                if (bettingNum.split("\\|").length == 3 || lotteryNum.split(",").length == 3) {
                    if (compareContainsStr(bettingNum, lotteryNum)) {
                        //中奖
                        win[0] = 1;
                    }
                }
                break;
            case 2:
                String[] t = bettingNum.split("\\|");
                if(t.length != 2)
                    break;
                String[] bettingNumArr = {t[0]+","+t[0]+","+t[1],t[0]+","+t[1]+","+t[0],t[1]+","+t[0]+","+t[0]};
                int i = Arrays.asList(bettingNumArr).indexOf(lotteryNum);
                if (i >= 0){
                    //中奖
                    win[1] = 1;
                }
                break;
            case 3:
                lotteryNum = getUnRepeatStr(lotteryNum);
                if (lotteryNum.split(",").length == 2){
                    if (containsNoSort(lotteryNum,bettingNum))
                        win[2]=1;
                }
                break;
            case 4:
                lotteryNum = getUnRepeatStr(lotteryNum);
                if(lotteryNum.split(",").length == 3){
                    if (containsNoSort(lotteryNum,bettingNum))
                        win[3] = 1;
                }
        }
        return win;
    }

    /**
    * @Author 苦智
    * @Description 双色球、大乐透中奖算法
    * @Date 10:17 2018/4/18
    * @Param [lotteryNum, bettingNum, playType, obj]
    * @return int[]
    **/
    protected int[] commonCal(String lotteryNum, String bettingNum, String playType,Object ... obj){
        int fNum = Integer.parseInt(obj[0].toString()), bNum = Integer.parseInt(obj[1].toString()),
                fMax = Integer.parseInt(obj[2].toString()), bMax = Integer.parseInt(obj[3].toString());
        int[][][] winners = (int[][][]) obj[4];
        String[] lotteryNums = lotteryNum.split("\\|");
        String[] bettingNums = bettingNum.split("\\|");
        if(lotteryNums.length != 2|| bettingNums.length != 2)
            return new int[]{0,0,0,0,0,0};
        String fReqNum = ""; //前区胆码
        String fOptNum = bettingNums[0]; //前区拖码
        String bReqNum = ""; //后区胆码
        String bOptNum = bettingNums[1]; //后区拖码

        //胆拖玩法
        if ("2".equals(playType)){
            int pos = bettingNums[0].indexOf(")");
            fReqNum = bettingNums[0].substring(1,pos);
            fOptNum = bettingNums[0].substring(pos+2);
            pos = bettingNums[1].indexOf(")");
            if (pos > 1) {
                bReqNum = bettingNums[1].substring(1, pos);
                bOptNum = bettingNums[1].substring(pos+2);
            }
        }

        //胆
        int[] fReq = hit(fReqNum,lotteryNums[0]);
        int[] bReq = hit(bReqNum,lotteryNums[1]);
        //拖
        int[] fOpt = hit(fOptNum, lotteryNums[0]);
        int[] bOpt = hit(bOptNum, lotteryNums[1]);

        if((fOpt[0]+fReq[0])>fMax||(bOpt[0]+bReq[0])>bMax||(fOpt[1]+fReq[1])>fNum||(bOpt[1]+bReq[1])>bNum){
            log.error("Params Error!");
            return null;
        }
        long[] fHits = solveHits(fNum, fReq[0], fOpt[0], fReq[1], fOpt[1]);
        long[] bHits = solveHits(bNum, bReq[0], bOpt[0], bReq[1], bOpt[1]);
        int[] result = new int[winners.length];
        for (int i = 0; i < winners.length; ++ i) {
            int[][] winner = winners[i];
            int count = 0;
            for (int j = 0; j < winner.length; ++ j) {
                int[] item = winner[j];
                count += fHits[item[0]] * bHits[item[1]];
            }
            result[i] = count;
        }
        return result;
    }

    public abstract String winInfo(int[] win, Integer betTimes);

    public abstract int[] win(String lotteryNum, String bettingNum, String playType);

}
